package consistenthash

import (
	"hash/crc32"
	"sort"
)

// HashFunc 定义hash函数，生成对应的hashcode，用于寻找hash环上的位置
type HashFunc func(data []byte) uint32

//NodeMapping 节点映射的结构体定义
type NodeMapping struct {
	hashFunc        HashFunc
	nodeHashes      []int
	nodeHashMapping map[int]string
}

// NewNodeMapping 构造hash节点映射的实例
func NewNodeMapping(f HashFunc) *NodeMapping {

	nodeMapping := &NodeMapping{
		hashFunc:        f,
		nodeHashMapping: make(map[int]string),
	}
	if f == nil {
		nodeMapping.hashFunc = crc32.ChecksumIEEE
	}
	return nodeMapping
}

//IsEmpty 判断当前的hash环上是否有节点
func (m *NodeMapping) IsEmpty() bool {
	return len(m.nodeHashMapping) == 0
}

//AddNode 节点加入到一致性hash的环上
func (m *NodeMapping) AddNode(nodes ...string) {
	for _, node := range nodes {
		if node == "" {
			continue
		}
		hashcode := m.hashFunc([]byte(node))
		hashIdx := int(hashcode)
		// 加入到hash值的切片中
		m.nodeHashes = append(m.nodeHashes, hashIdx)
		m.nodeHashMapping[hashIdx] = node
	}
	// 对加入到hash环中的nodes列表排序
	sort.Ints(m.nodeHashes)
}

//SelectNode 根据相关的redis的key值，选择相关的节点
func (m *NodeMapping) SelectNode(key string) string {
	if m.IsEmpty() {
		return ""
	}

	// 加入的key转为hash的值
	hashcode := m.hashFunc([]byte(key))
	// key转为相关的index
	hashIdx := int(hashcode)
	searchedIdx := sort.Search(len(m.nodeHashes), func(idx int) bool {
		return m.nodeHashes[idx] >= hashIdx
	})
	// 说明找到的节点的index超出nodeHashes切片的长度了
	if searchedIdx == len(m.nodeHashes) {
		// 重置为 idx = 0
		searchedIdx = 0
	}
	return m.nodeHashMapping[searchedIdx]
}
